package zjl;

import zjl.base.*;
import zjl.graph.Graph;
import zjl.util.Utility;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ResourceStore extends Graph {

    // NodeHost Collection
    private List<NodeHost> nodeHostList;
    // NodeSwitch Collection
    private List<NodeSwitch> nodeSwitchList;
    // DirectedLink Collection
    private List<DirectedLink> directedLinkList;

    // Business Request Collection
    private List<BizRequest> requests;
    private int clusterPeriod;

    public ResourceStore(String toponame, String bizname) {
        this.nodeHostList = new ArrayList<>();
        this.nodeSwitchList = new ArrayList<>();
        this.directedLinkList = new ArrayList<>();
        this.importTopoFromFile(toponame);

        this.requests = new ArrayList<>();
        this.importBizsFromFile(bizname);

        this.solveClusterPeriod();
    }

    public void solveClusterPeriod() {
        List<Integer> periods = new ArrayList<>();
        for (BizRequest r : requests) {
            periods.add(r.getPeriod());
        }

        this.clusterPeriod = Utility.LCM(periods);
    }

    public int getClusterPeriod() {
        return this.clusterPeriod;
    }

    public int getBizRequestSize() {
        return this.requests.size();
    }

    public BizRequest getBizRequestAt(int idx) {
        return this.requests.get(idx);
    }

    public NodeBase getNodeByName(String name) {
        for (NodeHost nh : this.nodeHostList) {
            if (nh.getName().equals(name))
                return nh;
        }

        for (NodeSwitch ns : this.nodeSwitchList) {
            if (ns.getName().equals(name))
                return ns;
        }

        return null;
    }

    private void importTopoFromFile(String tfn) {
        System.out.println("[INFO] IMPORT TOPOLOGY DATA FROM FILE.");
        try {
            FileReader fileReader = new FileReader(tfn);
            BufferedReader bufferedReader = new BufferedReader(fileReader);

            int flag = ConfigData.NEFLAG;
            String line = bufferedReader.readLine();

            while (line != null) {
                if (line.charAt(0) == ':') {
                    String[] temps = line.split("=");
                    String fstr = temps[1];

                    if (fstr.equals("HOST")) {
                        flag = ConfigData.NEFLAG;
                    } else if (fstr.equals("SWITCH")) {
                        flag = ConfigData.NSFLAG;
                    } else if (fstr.equals("LINK")) {
                        flag = ConfigData.LKFLAG;
                    } else {
                        return;
                    }
                } else {
                    if (flag == ConfigData.NEFLAG) {
                        String[] pts = line.split("-");
                        String hn = pts[0];
                        int pn = Integer.parseInt(pts[1]);
                        NodeHost ne = new NodeHost(hn, pn);
                        this.nodeHostList.add(ne);
                        this.addVertexInternal(ne);
                        System.out.println("ADD ES NODE [" + hn + "]");
                    } else if (flag == ConfigData.NSFLAG) {
                        String[] pts = line.split("-");
                        String sn = pts[0];
                        int pn = Integer.parseInt(pts[1]);
                        NodeSwitch ns = new NodeSwitch(sn, pn);
                        this.nodeSwitchList.add(ns);
                        this.addVertexInternal(ns);
                        System.out.println("ADD NS NODE [" + sn + "]");
                    } else if (flag == ConfigData.LKFLAG) {
                        String[] strList = line.split(" ");

                        String bpn = strList[0];
                        String epn = strList[1];
                        double we = Double.parseDouble(strList[2]);
                        int bw = Integer.parseInt(strList[3]); // bps

                        String bn = bpn.split("-")[0];
                        String en = epn.split("-")[0];
                        NodeBase begnb = this.getNodeByName(bn);
                        NodeBase endnb = this.getNodeByName(en);

                        DirectedLink link1 = new DirectedLink(begnb, endnb, bpn, epn, we, bw);
                        this.directedLinkList.add(link1);
                        this.addEdgeInternal(link1);

                        System.out.println("ADD FW LINK[" + bn + "->" + en + "]");

                        DirectedLink link2 = new DirectedLink(endnb, begnb, epn, bpn, we, bw);
                        this.directedLinkList.add(link2);
                        this.addEdgeInternal(link2);

                        System.out.println("ADD BW LINK[" + en + "->" + bn + "]");
                    }
                }

                line = bufferedReader.readLine();
            }

            bufferedReader.close();
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void importBizsFromFile(String rfn) {
        System.out.println("[INFO] IMPORT BUSINESS DATA FROM FILE.");
        try {
            FileReader fileReader = new FileReader(rfn);
            BufferedReader bufferedReader = new BufferedReader(fileReader);

            int flag = ConfigData.BZFLAG;
            String line = bufferedReader.readLine();

            while (line != null) {
                if (line.charAt(0) == ':') {
                    String[] temps = line.split("=");

                    String rstr = temps[1];
                    if (rstr.equals("BIZ")) {
                        flag = ConfigData.BZFLAG;
                    }
                } else {
                    if (flag == ConfigData.BZFLAG) {
                        String[] strList = line.split(" ");

                        String bn = strList[0];
                        String en = strList[1];
                        int length = Integer.parseInt(strList[2]);
                        int period = Utility.upmod(Double.parseDouble(strList[3]), ConfigData.SLOT_LENGTH);
                        int deadline = Utility.upmod(Double.parseDouble(strList[4]), ConfigData.SLOT_LENGTH);

                        NodeBase begnb = this.getNodeByName(bn);
                        NodeBase endnb = this.getNodeByName(en);

                        BizRequest request = new BizRequest();
                        request.setSrc((NodeHost) begnb);
                        request.setDest((NodeHost) endnb);
                        request.setPeriod(period);
                        request.setDeadline(deadline);
                        request.setLength(length);

                        this.requests.add(request);

                        System.out.println("ADD BIZ REQ[" + bn + "->" + en +"]");
                    }
                }
                line = bufferedReader.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
